/**
 * 使用worker_threads实现的多线程热键检测器
 */
const { Worker } = require('worker_threads');
const SharedSlidingWindow = require('./SharedSlidingWindow');
const os = require('os');
const NodeCache = require('node-cache');
const KeyRuleHolder = require('./KeyRuleHolder');
const { KeyEventOriginal } = require('./IKeyListener');
const { ConsolePusher } = require('./Pusher');

/**
 * 多线程热键检测器
 * 使用worker_threads和SharedArrayBuffer实现线程间数据共享
 */
class ThreadsHotKeyDetector {
  constructor() {
    // 工作线程数量
    this.workerCount = os.cpus().length;
    
    // 工作线程池
    this.workers = [];
    
    // 工作线程就绪状态
    this.workerReady = new Array(this.workerCount).fill(false);
    
    // 热键缓存
    this.hotCache = new NodeCache({ stdTTL: 60, checkperiod: 120 });
    
    // 滑动窗口缓存 (按应用名缓存)
    this.windowCaches = new Map();
    
    // 推送器列表
    this.pushers = [new ConsolePusher()];
    
    // 分隔符
    this.SPLITER = '-';
    
    // 日志开关
    this.loggerOn = true;
    
    // 日志采样率 (每x次事件输出一次日志)
    this.logSampleRate = 100;
    
    // 日志计数器 (用于采样)
    this.logCounter = new Map();
    
    // 日志时间间隔 (单位：毫秒，同一个key在此时间段内只打印一次)
    this.logTimeInterval = 5000;
    
    // 上次日志时间记录
    this.lastLogTime = new Map();
    
    // 初始化工作线程池
    this._initWorkers();
    
    // 记录就绪状态
    this.isReady = false;
    
    // 检查是否所有工作线程已就绪
    this._checkWorkersReady();
  }
  
  /**
   * 初始化工作线程池
   * @private
   */
  _initWorkers() {
    for (let i = 0; i < this.workerCount; i++) {
      try {
        // 为每个工作线程创建一个初始的共享内存
        // 计算足够大的共享内存空间：
        // - 20个时间片计数（支持最大10秒的窗口，每秒2个片）
        // - 4个时间戳字段（起始时间秒/毫秒和最后添加时间秒/毫秒）
        const bufferSize = Int32Array.BYTES_PER_ELEMENT * 24; 
        const sharedBuffer = new SharedArrayBuffer(bufferSize);
        
        // 创建工作线程
        const worker = new Worker('./SharedSlidingWindow.js', {
          workerData: { 
            index: i,
            buffer: sharedBuffer,
            duration: 10,
            threshold: 100
          }
        });
        
        // 处理工作线程的消息
        worker.on('message', (message) => {
          // 处理工作线程就绪状态
          if (message.status === 'ready') {
            const workerIndex = message.index;
            this.workerReady[workerIndex] = true;
            console.log(`工作线程 ${workerIndex} 已就绪`);
            this._checkWorkersReady();
            return;
          }
          
          // 处理错误消息
          if (message.error) {
            console.error(`工作线程错误: ${message.error}`);
            return;
          }
          
          // 处理热键消息
          if (message.isHot) {
            // 如果是热键，处理热键事件
            this._handleHotKey(message);
            return;
          }
        });
        
        // 处理工作线程错误
        worker.on('error', (error) => {
          console.error(`工作线程 ${i} 出错:`, error);
        });
        
        this.workers.push(worker);
      } catch (err) {
        console.error(`初始化工作线程 ${i} 失败:`, err);
      }
    }
    
    console.log(`初始化了 ${this.workerCount} 个工作线程`);
  }
  
  /**
   * 处理热键事件
   * @param {Object} message - 工作线程发送的消息
   * @private
   */
  _handleHotKey(message) {
    const { key, isHot } = message;
    
    // 解析key (格式: appName-keyType-key)
    const parts = key.split(this.SPLITER);
    const appName = parts[0];
    const keyType = parts[1];
    const actualKey = parts.slice(2).join(this.SPLITER); // 原始key可能包含分隔符，所以需要重新连接
    
    // 构建热键模型
    const hotKeyModel = {
      key: actualKey,
      appName,
      keyType,
      createTime: Date.now()
    };
    
    // 如果这个key已经被标记为热键，跳过
    if (this.hotCache.get(key)) {
      return;
    }
    
    // 标记为热键
    this.hotCache.set(key, 1);
    
    // 记录日志
    if (this.loggerOn) {
      console.log(`新热键事件: ${actualKey}`);
    }
    
    // 推送到各客户端
    for (const pusher of this.pushers) {
      pusher.push(hotKeyModel);
    }
  }
  
  /**
   * 设置推送器
   * @param {Array} pushers - 推送器列表
   */
  setPushers(pushers) {
    this.pushers = pushers;
  }
  
  /**
   * 设置日志开关
   * @param {boolean} on - 是否开启
   */
  setLoggerOn(on) {
    this.loggerOn = on;
  }
  
  /**
   * 处理新的键
   * @param {Object} hotKeyModel - 热键模型
   * @param {string} original - 来源
   */
  newKey(hotKeyModel, original) {
    try {
      // 检查系统是否准备好
      if (!this.isReady) {
        if (this.loggerOn) {
          console.log('系统尚未完全就绪，键事件将被忽略');
        }
        return;
      }
      
      // 构建缓存键
      const key = this._buildKey(hotKeyModel);
      
      // 判断是不是刚热不久
      if (this.hotCache.get(key)) {
        return;
      }
      
      // 获取该键的规则
      const keyRule = KeyRuleHolder.getRuleByAppAndKey(hotKeyModel);
      
      // 获取或创建该键的滑动窗口配置
      const windowConfig = this._getOrCreateSlidingWindow(key, keyRule.interval, keyRule.threshold);
      
      // 计算工作线程索引 (简单的哈希分配)
      const workerIndex = this._getWorkerIndex(key);
      
      // 确保工作线程索引有效
      if (workerIndex >= this.workers.length) {
        console.error(`无效的工作线程索引: ${workerIndex}, 总线程数: ${this.workers.length}`);
        return;
      }
      
      // 确保选择的工作线程已就绪
      if (!this.workerReady[workerIndex]) {
        console.error(`工作线程 ${workerIndex} 尚未就绪`);
        return;
      }
      
      // 发送计数请求和滑动窗口配置到工作线程
      this.workers[workerIndex].postMessage({
        cmd: 'addCount',
        key: key,
        count: hotKeyModel.count || 1,
        buffer: windowConfig.buffer,
        interval: windowConfig.interval,
        threshold: windowConfig.threshold
      });
      
      // 事件触发式日志，使用采样率控制日志输出
      if (this.loggerOn && this._shouldLogKeyEvent(key)) {
        console.log(`键事件: ${key}, 计数: ${hotKeyModel.count || 1}, 工作线程: ${workerIndex}`);
      }
    } catch (err) {
      console.error('处理新键时出错:', err);
    }
  }
  
  /**
   * 删除键
   * @param {Object} hotKeyModel - 热键模型
   * @param {string} original - 来源
   */
  removeKey(hotKeyModel, original) {
    // 构建缓存键
    const key = this._buildKey(hotKeyModel);
    
    // 从热键缓存移除
    this.hotCache.del(key);
    
    // 设置删除时间
    hotKeyModel.createTime = Date.now();
    
    // 记录日志
    console.log(`键删除事件: ${hotKeyModel.key}`);
    
    // 推送删除事件到各客户端
    for (const pusher of this.pushers) {
      pusher.remove(hotKeyModel);
    }
  }
  
  /**
   * 构建缓存键
   * @param {Object} hotKeyModel - 热键模型
   * @returns {string} 缓存键
   * @private
   */
  _buildKey(hotKeyModel) {
    return [hotKeyModel.appName, hotKeyModel.keyType, hotKeyModel.key].join(this.SPLITER);
  }
  
  /**
   * 计算工作线程索引
   * @param {string} key - 键
   * @returns {number} - 工作线程索引
   * @private
   */
  _getWorkerIndex(key) {
    // 简单的哈希算法，将键映射到工作线程
    let hash = 0;
    for (let i = 0; i < key.length; i++) {
      hash = ((hash << 5) - hash) + key.charCodeAt(i);
      hash |= 0; // 转换为32位整数
    }
    return Math.abs(hash) % this.workerCount;
  }
  
  /**
   * 获取或创建滑动窗口
   * @param {string} key - 键
   * @param {number} interval - 窗口间隔
   * @param {number} threshold - 阈值
   * @returns {Object} - 包含滑动窗口信息的对象
   * @private
   */
  _getOrCreateSlidingWindow(key, interval, threshold) {
    // 从应用缓存中获取
    const appName = key.split(this.SPLITER)[0];
    let appCache = this.windowCaches.get(appName);
    
    if (!appCache) {
      appCache = new Map();
      this.windowCaches.set(appName, appCache);
    }
    
    // 从缓存中获取滑动窗口配置
    let windowConfig = appCache.get(key);
    
    // 如果滑动窗口不存在，创建新的
    if (!windowConfig) {
      // 为新的滑动窗口创建共享内存
      // 计算足够大的共享内存大小
      // - 20个时间片计数（支持最大10秒窗口，每秒2个片）
      // - 4个时间戳字段（起始时间秒/毫秒和最后添加时间秒/毫秒）
      const bufferSize = Int32Array.BYTES_PER_ELEMENT * 24;
      const sharedBuffer = new SharedArrayBuffer(bufferSize);
      
      // 创建新的滑动窗口配置
      windowConfig = {
        buffer: sharedBuffer,
        interval: interval,
        threshold: threshold
      };
      
      // 初始化滑动窗口
      const slidingWindow = new SharedSlidingWindow(interval, threshold);
      slidingWindow.sharedArrayBuffer = sharedBuffer;
      slidingWindow.sharedArray = new Int32Array(sharedBuffer);
      slidingWindow.reset(); // 初始化共享内存
      
      // 缓存配置
      appCache.set(key, windowConfig);
    }
    
    return windowConfig;
  }
  
  /**
   * 检查所有工作线程是否已就绪
   * @private
   */
  _checkWorkersReady() {
    // 检查所有工作线程是否都已就绪
    const allReady = this.workerReady.every(ready => ready);
    
    if (allReady && !this.isReady) {
      this.isReady = true;
      console.log('所有工作线程已就绪，系统启动完成');
    }
  }
  
  /**
   * 判断是否应该打印键事件日志
   * 基于采样率和时间间隔控制日志输出频率
   * @param {string} key - 键
   * @returns {boolean} - 是否应该打印日志
   * @private
   */
  _shouldLogKeyEvent(key) {
    const now = Date.now();
    
    // 基于时间间隔控制 - 同一个key在指定时间间隔内只打印一次
    const lastTime = this.lastLogTime.get(key) || 0;
    if (now - lastTime < this.logTimeInterval) {
      return false;
    }
    
    // 基于采样率控制 - 全局计数，每N次事件只打印一次
    const count = this.logCounter.get(key) || 0;
    this.logCounter.set(key, (count + 1) % this.logSampleRate);
    
    if (count === 0) {
      // 更新该key的最后日志时间
      this.lastLogTime.set(key, now);
      return true;
    }
    
    return false;
  }
  
  /**
   * 设置日志采样率
   * @param {number} sampleRate - 采样率，每多少次事件打印一次日志
   */
  setLogSampleRate(sampleRate) {
    if (typeof sampleRate === 'number' && sampleRate > 0) {
      this.logSampleRate = sampleRate;
    }
  }
  
  /**
   * 设置日志时间间隔
   * @param {number} interval - 时间间隔(毫秒)，同一个key在此时间段内只打印一次
   */
  setLogTimeInterval(interval) {
    if (typeof interval === 'number' && interval > 0) {
      this.logTimeInterval = interval;
    }
  }
  
  /**
   * 关闭所有工作线程
   * @returns {Promise<void>} - 返回一个Promise，在所有工作线程关闭后解决
   */
  close() {
    console.log('正在关闭所有工作线程...');
    
    // 清理缓存资源
    this.hotCache.close();
    this.windowCaches.clear();
    this.logCounter.clear();
    this.lastLogTime.clear();
    
    // 使用Promise等待所有工作线程关闭
    const workerClosePromises = [];
    
    // 向所有工作线程发送关闭消息
    for (let i = 0; i < this.workers.length; i++) {
      try {
        const worker = this.workers[i];
        if (worker) {
          const workerIndex = i;
          
          // 为每个工作线程创建一个Promise
          const closePromise = new Promise((resolve) => {
            // 先尝试优雅地发送关闭消息
            worker.postMessage({ cmd: 'shutdown' });
            
            // 设置超时，如果工作线程没有自行结束，则强制终止
            const terminateTimeout = setTimeout(() => {
              try {
                console.log(`工作线程 ${workerIndex} 未响应，强制终止`);
                worker.terminate();
              } catch (err) {
                console.error(`强制终止工作线程 ${workerIndex} 时出错:`, err);
              } finally {
                resolve();
              }
            }, 1000);
            
            // 如果工作线程优雅退出，清除超时定时器
            worker.once('exit', () => {
              clearTimeout(terminateTimeout);
              console.log(`工作线程 ${workerIndex} 已退出`);
              resolve();
            });
          });
          
          workerClosePromises.push(closePromise);
        }
      } catch (err) {
        console.error(`关闭工作线程 ${i} 时出错:`, err);
      }
    }
    
    // 使用Promise.all等待所有工作线程关闭
    return Promise.all(workerClosePromises)
      .then(() => {
        // 重置状态
        this.workers = [];
        this.workerReady = [];
        this.isReady = false;
        console.log('已关闭所有工作线程');
      });
  }
}

module.exports = ThreadsHotKeyDetector;
